/**
* @file    hi_hal_touchpad.c
* @brief   hal touchapd implemention
*
* Copyright (c) 2017 Huawei Tech.Co.,Ltd
*
* @author    HiMobileCam Reference Develop Team
* @date      2017/12/23
* @version   1.0

*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/input.h>
#include <errno.h>
#include <sys/wait.h>

#include "hi_appcomm_log.h"
#include "hi_appcomm_util.h"

#include "hi_hal_common.h"
#include "hi_hal_touchpad.h"


#ifdef __cplusplus
#if __cplusplus
    extern "C"{
#endif
#endif  /* End of #ifdef __cplusplus */


static HI_BOOL g_touchpadInitState = HI_FALSE; /**<  touchpad init state */
static HI_S32 g_touchpadInputFd = HAL_FD_INITIALIZATION_VAL;
static HI_BOOL g_touchpadEnable = HI_TRUE;     /**< touchpad enable flag */
static HI_S32 g_touchpadFd = HAL_FD_INITIALIZATION_VAL;

/* check touchpad init or not */
#define HAL_TOUCHPAD_CHECKINIT() do { \
        if (!g_touchpadInitState) { \
            MLOGE("TouchPad has not been inited\n"); \
            return HI_HAL_ENOINIT;  \
        }   \
    } while(0)

#define HAL_TOUCHPAD_CHECKFD() do { \
        if (g_touchpadInputFd == HAL_FD_INITIALIZATION_VAL) { \
            MLOGE("TouchPad has not been started\n"); \
            return HI_HAL_ENOINIT;  \
        }   \
    } while(0)

/** macro define */
#define HAL_TOUCHPAD_KOPATH        HI_APPFS_KOMOD_PATH"/hi_virtualtp.ko"
#define HAL_TOUCHAPD_INPUTDEV      ("/dev/input/event0")
#define HAL_TOUCHAPD_DEV           ("/dev/hi_virtualtp")
#define HAL_TOUCHAPD_MAXPOINTNUM   (1)
#define HAL_TOUCHAPD_BITLONG       (32)
#define HAL_TOUCHAPD_BITWORD(a)    ((a) / HAL_TOUCHAPD_BITLONG)

/** multi touch info */
typedef struct tagHAL_TOUCHPAD_MtSampleInfo
{
    HI_S32 touchID;
    HI_S32 x;
    HI_S32 y;
    HI_S32 touchPressure;
    struct timeval timeInterval;
} HAL_TOUCHPAD_MtSampleInfo;

typedef struct tagHAL_TOUCHPAD_MtInputInfo
{
    HI_S32 touchFilled[HAL_TOUCHAPD_MAXPOINTNUM];/**< MARK filled flag,array index is id */
    HI_S32 x[HAL_TOUCHAPD_MAXPOINTNUM];
    HI_S32 y[HAL_TOUCHAPD_MAXPOINTNUM];
    HI_S32 touchPressure[HAL_TOUCHAPD_MAXPOINTNUM];
    HI_S32 touchSlot;                             /**< represent input id */
} HAL_TOUCHPAD_MtInputInfo;

static  HAL_TOUCHPAD_MtInputInfo g_touchpadInputInfo;

static HI_VOID HAL_TOUCHPAD_PinoutInit(HI_VOID)
{
    MLOGD("Set Pin Reuse OK.\n\n");
}

static inline HI_S32 HAL_TOUCHPAD_InputBitCheck(HI_S32 bit,
    const volatile HI_U32* addr)
{
    return 1UL & (addr[HAL_TOUCHAPD_BITWORD(bit)] >> (bit & (HAL_TOUCHAPD_BITLONG - 1)));
}

static HI_S32 HAL_TOUCHPAD_PushSamp(HAL_TOUCHPAD_MtInputInfo* inputInfo,
    struct input_event* inputEvent, HAL_TOUCHPAD_MtSampleInfo* sampInfo)
{
    HI_S32 touchNum = 0;
    HI_S32 touchSlot = 0;
    for (touchSlot = 0; touchSlot < HAL_TOUCHAPD_MAXPOINTNUM; ++touchSlot) {
        if (!inputInfo->touchFilled[touchSlot]) {
            continue;/**<touchSlot index has no upadte data*/
        }
        sampInfo[touchNum].x = inputInfo->x[touchSlot];
        sampInfo[touchNum].y = inputInfo->y[touchSlot];
        sampInfo[touchNum].touchPressure= inputInfo->touchPressure[touchSlot];
        sampInfo[touchNum].timeInterval = inputEvent->time;
        sampInfo[touchNum].touchID = touchSlot;

        inputInfo->touchFilled[touchSlot] = 0;
        touchNum++;
    }
    return touchNum;
}

static HI_S32 HAL_TOUCHPAD_ABSEventFillInputInfo(struct input_event* inputEvent)
{
    switch (inputEvent->code)
    {
        case ABS_PRESSURE:
            (&g_touchpadInputInfo)->touchPressure[(&g_touchpadInputInfo)->touchSlot] = inputEvent->value;
            break;
        case ABS_MT_SLOT:
            if (inputEvent->value < 0) {
                break;
            }
            (&g_touchpadInputInfo)->touchSlot = inputEvent->value;
            if ((&g_touchpadInputInfo)->touchSlot >= HAL_TOUCHAPD_MAXPOINTNUM) {
                (&g_touchpadInputInfo)->touchSlot = 0;
            }
            break;
        case ABS_MT_TOUCH_MAJOR:
            (&g_touchpadInputInfo)->touchPressure[(&g_touchpadInputInfo)->touchSlot] = inputEvent->value;
            (&g_touchpadInputInfo)->touchFilled[(&g_touchpadInputInfo)->touchSlot] = 1;
            break;
        case ABS_MT_POSITION_X:
            (&g_touchpadInputInfo)->x[(&g_touchpadInputInfo)->touchSlot] = inputEvent->value;
            (&g_touchpadInputInfo)->touchPressure[(&g_touchpadInputInfo)->touchSlot] = 1;
            (&g_touchpadInputInfo)->touchFilled[(&g_touchpadInputInfo)->touchSlot] = 1;
            break;
        case ABS_MT_POSITION_Y:
            (&g_touchpadInputInfo)->y[(&g_touchpadInputInfo)->touchSlot] = inputEvent->value;
            (&g_touchpadInputInfo)->touchPressure[(&g_touchpadInputInfo)->touchSlot] = 1;
            (&g_touchpadInputInfo)->touchFilled[(&g_touchpadInputInfo)->touchSlot] = 1;
            break;
        case ABS_MT_TRACKING_ID:
            if (inputEvent->value == -1) {
                (&g_touchpadInputInfo)->touchFilled[(&g_touchpadInputInfo)->touchSlot] = 1;
            }
            break;
    }
    return HI_SUCCESS;
}

static HI_S32 HAL_TOUCHPAD_ReadInputEventStatic(HAL_TOUCHPAD_MtSampleInfo* sampInfo)
{
    struct input_event inputEvent;
    HI_S32 ret = HI_SUCCESS;
    HI_BOOL dataReadCompleted = HI_FALSE;
    HI_S32 pushed = 0;

    while (HI_FALSE == dataReadCompleted) {
        ret = read(g_touchpadInputFd, &inputEvent, sizeof(struct input_event));

        if (ret < (HI_S32)sizeof(struct input_event)) {
            /** no more data */
            MLOGW("ret(%d) <sizeof(struct input_event)(%d)\n",(HI_S32)ret,(HI_S32)sizeof(struct input_event));
            break;
        }

        switch (inputEvent.type) {
                /** key 0x1*/
            case EV_KEY:
                switch (inputEvent.code) {
                        /** 14a 0 */
                    case BTN_TOUCH:
                        (&g_touchpadInputInfo)->touchPressure[(&g_touchpadInputInfo)->touchSlot] = inputEvent.value;
                        (&g_touchpadInputInfo)->touchFilled[(&g_touchpadInputInfo)->touchSlot] = 1;
                        break;
                }

                break;

                /** 0x00 */
            case EV_SYN:
                switch (inputEvent.code) {
                    case SYN_REPORT:
                    case SYN_MT_REPORT:
                    default:
                    {
                        pushed = HAL_TOUCHPAD_PushSamp(&g_touchpadInputInfo, &inputEvent, sampInfo);
                        sampInfo += pushed;
                        dataReadCompleted = HI_TRUE;
                    }
                    break;
                }
                break;
                /** 0x3 */
            case EV_ABS:
                HAL_TOUCHPAD_ABSEventFillInputInfo(&inputEvent);
                break;
        }
    }
    return HI_SUCCESS;
}

HI_S32 HI_HAL_TOUCHPAD_Init(HI_VOID)
{
    HI_S32 ret = HI_SUCCESS;
    if (g_touchpadInitState) {
        MLOGE("touchapd already init\n");
        return HI_HAL_EINITIALIZED;
    }
    HAL_TOUCHPAD_PinoutInit();
    /** insmod touchpad driver */
    ret = HI_insmod(HAL_TOUCHPAD_KOPATH, NULL);
    if(ret != 0) {
        MLOGE("insmod touchpad:failed, errno(%d)\n", errno);
        return HI_HAL_EINVOKESYS;
    }

    g_touchpadFd = open(HAL_TOUCHAPD_DEV, O_RDWR);
    if (HAL_FD_INITIALIZATION_VAL == g_touchpadFd) {
        HI_rmmod(HAL_TOUCHPAD_KOPATH);
        return HI_HAL_EINTER;
    }

    g_touchpadInitState = HI_TRUE;
    return HI_SUCCESS;
}

HI_S32 HI_HAL_TOUCHPAD_SetRotate( HI_HAL_TOUCHPAD_ROTATE_E enRotate)
{
    HAL_TOUCHPAD_CHECKINIT();
    MLOGE("[Errot] Set Rotate not support at present\n");
    return HI_FAILURE;
}

HI_S32 HI_HAL_TOUCHPAD_Suspend(HI_VOID)
{
    HAL_TOUCHPAD_CHECKINIT();
    g_touchpadEnable = HI_FALSE;
    MLOGD("touchpad suspend\n");
    return HI_SUCCESS;
}

HI_S32 HI_HAL_TOUCHPAD_Resume(HI_VOID)
{
    HAL_TOUCHPAD_CHECKINIT();
    g_touchpadEnable = HI_TRUE;
    MLOGD("touchpad resumme\n");
    return HI_SUCCESS;
}

HI_S32 HI_HAL_TOUCHPAD_Start(HI_S32* ps32Fd)
{
    HAL_TOUCHPAD_CHECKINIT();
    HI_APPCOMM_CHECK_POINTER(ps32Fd, HI_HAL_EINVAL);

    HI_S32 ret = HI_SUCCESS;
    HI_U32 inputBits[(ABS_MAX + 31) / 32];
    if (g_touchpadInputFd != HAL_FD_INITIALIZATION_VAL) {
        MLOGE("touchpad already started\n");
        return HI_HAL_ENOSTART;
    }
    memset(&g_touchpadInputInfo, 0, sizeof(HAL_TOUCHPAD_MtInputInfo));
    g_touchpadInputFd = open(HAL_TOUCHAPD_INPUTDEV, O_RDONLY);
    if (g_touchpadInputFd < 0) {
        MLOGE("open /dev/input/event0 err,errno(%d)\n",errno);
        return HI_HAL_EINVOKESYS;
    }

    ret = ioctl(g_touchpadInputFd, EVIOCGBIT(EV_ABS, sizeof(inputBits)), inputBits);
    if (ret < 0) {
        MLOGE("open ioctl err,errno(%d)\n",errno);
        close(g_touchpadInputFd);
        g_touchpadInputFd = HAL_FD_INITIALIZATION_VAL;
        return HI_HAL_EINVOKESYS;
    }

    if ((HAL_TOUCHPAD_InputBitCheck(ABS_MT_POSITION_X, inputBits) == 0)
         ||(HAL_TOUCHPAD_InputBitCheck(ABS_MT_POSITION_Y, inputBits) == 0)
         ||(HAL_TOUCHPAD_InputBitCheck(ABS_MT_TOUCH_MAJOR, inputBits) == 0)) {
        MLOGE("error: could not support the device\n");
        MLOGE("EV_SYN=%d\n", HAL_TOUCHPAD_InputBitCheck(EV_SYN, inputBits));
        MLOGE("EV_ABS=%d\n", HAL_TOUCHPAD_InputBitCheck(EV_ABS, inputBits));
        MLOGE("ABS_MT_POSITION_X=%d\n", HAL_TOUCHPAD_InputBitCheck(ABS_MT_POSITION_X, inputBits));
        MLOGE("ABS_MT_POSITION_Y=%d\n", HAL_TOUCHPAD_InputBitCheck(ABS_MT_POSITION_Y, inputBits));
        MLOGE("ABS_MT_TOUCH_MAJOR=%d\n", HAL_TOUCHPAD_InputBitCheck(ABS_MT_TOUCH_MAJOR, inputBits));
        close(g_touchpadInputFd);
        g_touchpadInputFd = HAL_FD_INITIALIZATION_VAL;
        return HI_HAL_EINTER;
    }
    *ps32Fd = g_touchpadInputFd;
    return HI_SUCCESS;
}

#if defined(CFG_TOUCHPAD_VIRTUAL)
HI_S32 HI_HAL_TOUCHPAD_SetCoordinate(const hi_virtualtp_twospot* virtualSpot)
{
    HI_APPCOMM_CHECK_POINTER(virtualSpot, HI_HAL_EINVAL);
    HI_S32 ret = HI_SUCCESS;
    ret = ioctl(g_touchpadFd, VIRTUALTP_SET_COORDINATE, virtualSpot);
    if (ret != HI_SUCCESS) {
        MLOGE("VIRTUALTP_SET_COORDINATE failed\n");
        close(g_touchpadFd);
        return HI_HAL_EINTER ;
    }
    return HI_SUCCESS;
}
#endif

HI_S32 HI_HAL_TOUCHPAD_Stop(HI_VOID)
{
    HAL_TOUCHPAD_CHECKINIT();
    HAL_TOUCHPAD_CHECKFD();

    HI_S32 ret = HI_SUCCESS;
    ret = close(g_touchpadInputFd);
    if (ret != HI_SUCCESS) {
        MLOGD("close err,errno(%d)\n",errno);
        return HI_HAL_EINVOKESYS;
    }
    g_touchpadInputFd = HAL_FD_INITIALIZATION_VAL;
    return HI_SUCCESS;
}

HI_S32 HI_HAL_TOUCHPAD_ReadInputEvent(HI_HAL_TOUCHPAD_INPUTINFO_S* pstInputData)
{
    HI_APPCOMM_CHECK_POINTER(pstInputData, HI_HAL_EINVAL);
    HAL_TOUCHPAD_CHECKINIT();
    HAL_TOUCHPAD_CHECKFD();

    HI_S32 ret = HI_SUCCESS;
    HAL_TOUCHPAD_MtSampleInfo sampInfo[HAL_TOUCHAPD_MAXPOINTNUM];
    memset(sampInfo, 0, sizeof(HAL_TOUCHPAD_MtSampleInfo) * HAL_TOUCHAPD_MAXPOINTNUM);

    ret = HAL_TOUCHPAD_ReadInputEventStatic(sampInfo);
    if (ret != HI_SUCCESS) {
        MLOGD("read_input_event err\n");
        return HI_HAL_EINTER;
    }

    if (g_touchpadEnable) {
        pstInputData->s32ID = sampInfo[0].touchID;
        pstInputData->u32Pressure = sampInfo[0].touchPressure;
        pstInputData->s32X = sampInfo[0].x;
        pstInputData->s32Y = sampInfo[0].y;
        pstInputData->u32TimeStamp = (sampInfo[0].timeInterval.tv_sec) * 1000 + (sampInfo[0].timeInterval.tv_usec) / 1000;
    } else {
        pstInputData->s32ID = 0;
        pstInputData->u32Pressure = 0;
        pstInputData->s32X = 0;
        pstInputData->s32Y = 0;
        pstInputData->u32TimeStamp = 0;
    }
    return HI_SUCCESS;
}

HI_S32 HI_HAL_TOUCHPAD_Deinit(HI_VOID)
{
    HAL_TOUCHPAD_CHECKINIT();
    if (g_touchpadInputFd != HAL_FD_INITIALIZATION_VAL) {
        MLOGE("touchpad need stop first\n");
        return HI_HAL_ENOSTART;
    }

    HI_S32 ret = HI_SUCCESS;
    /** Deinitial touchpad Device */
    ret = HI_rmmod(HAL_TOUCHPAD_KOPATH);
    if(ret != HI_SUCCESS) {
        MLOGE("rmmod touchpad:failed, errno(%d)\n", ret);
        return HI_HAL_EINVOKESYS;
    }
    close(g_touchpadFd);
    g_touchpadInitState = HI_FALSE;
    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
    }
#endif
#endif /* __cplusplus */
